#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Socket.hpp"

using namespace SocketModule;
using namespace LogModule;

#define NUM sizeof(fd_set) * 8

const int gdefaultfd = -1;
#define MAX 4096

// 最开始的时候，tcpserver只有一个listensockfd
class EpollServer
{
    const static int revs_num = 64;
public:
    EpollServer(int port)
        : _port(port),
          _listen_socket(std::make_unique<TcpSocket>()),
          _isrunning(false)
    {
    }
    void Init()
    {
        _listen_socket->BuildTcpSocketMethod(_port);
        _efd = epoll_create(256);
        if(_efd < 0)
        {
            LOG(LogLevel::DEBUG)<<"epoll_create false";
            return ;
        }
        LOG(LogLevel::DEBUG)<<"epoll_create success";

        struct epoll_event event;
        event.events =EPOLLIN;
        event.data.fd = _listen_socket->Fd();
        //要把listen套接字的fd添加给efd里面
        int n = epoll_ctl(_efd,EPOLL_CTL_ADD,event.data.fd,&event);
        if(n<0)
        {
            LOG(LogLevel::FATAL)<<"epoll_ctl add false";
            return;
        }
        LOG(LogLevel::ERROR)<<"epoll_ctl add success";



    }
    void Loop()
    {
        int timeout = -1;
        _isrunning = true;
        while (_isrunning)
        {
            
            //wait 的返回值是个数，而且等到的话，会把对应的文件描述符，调到前面
            int n = epoll_wait(_efd,_revs,revs_num,timeout);
            switch (n)
            {
            case 0:
                std::cout << "time out..." << std::endl;
                break;

            case -1:
                std::cout << "客户端退出了" << std::endl;
                break;

            default:
                std::cout << "有事件就绪了" << std::endl;
                Dispatcher(n);
                break;
            }

            // 我们不能让accept来阻塞检测新连接到来，而应该让select来负责进行就绪事件的检测
            // 用户告诉内核，你要帮我关心&rfds，读事件啊
        }
        _isrunning = false;
    }
    void Accepter() // 回调函数呢？
    {
        InetAddr client;
        // listensockfd就绪了！获取新连接不就好了吗？
        int newfd = _listen_socket->Accepter(&client); // 会不会被阻塞呢？不会！select已经告诉我，listensockfd已经就绪了！只执行"拷贝"
        if (newfd < 0)
            return;
        else
        {
            std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;
            // recv()?? 读事件是否就绪，我们并不清楚！newfd也托管给select，让select帮我进行关心新的sockfd上面的读事件就绪
            // 怎么把新的newfd托管给select?让select帮我去关心newfd上面的读事件呢？把newfd，添加到辅助数组即可！
            struct epoll_event event;
            event.events = EPOLLIN;
            event.data.fd = newfd;

            int n = epoll_ctl(_efd,EPOLL_CTL_ADD,newfd,&event);
            if(n<0)
            {
                LOG(LogLevel::FATAL)<<"epoll_ctl add false";
                return;
            }
            LOG(LogLevel::ERROR)<<"epoll_ctl add success";
            
        }
    }
    void Recver(int who) // 回调函数？
    {
        // 合法的，就绪的，普通的fd
        // 这里的recv，对不对啊！不完善！必须得有协议！
        int fd = _revs[who].data.fd;
        char buffer[1024];
        ssize_t n = recv(fd , buffer, sizeof(buffer) - 1, 0); // 会不会被阻塞？就绪了
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# " << buffer << std::endl;
            // 把读到的信息，在回显会去
            std::string message = "echo# ";
            message += buffer;
            send(_revs[who].data.fd, message.c_str(), message.size(), 0); // bug
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出, sockfd: " << fd ;
           
            int m = epoll_ctl(_efd,EPOLL_CTL_DEL,fd,nullptr);
            if(m < 0)
            {
                LOG(LogLevel::FATAL)<<"Epoll_ctl del false";
                return;
            }
            LOG(LogLevel::DEBUG)<<"Epoll_ctl success";
            close(fd);//注意这里一定要先在_revs里面把fd删掉，再关闭文件描述符，
            //因为epoll_ctl 再里面会审查这个文件描述符是否合法。
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: " << fd;
             int m = epoll_ctl(_efd,EPOLL_CTL_DEL,fd,nullptr);
            if(m < 0)
            {
                LOG(LogLevel::FATAL)<<"Epoll_ctl del false";
                return;
            }
            LOG(LogLevel::DEBUG)<<"Epoll_ctl success";
            close(fd);
        }
    }
    void Dispatcher(int waitnum) // rfds就可能会有更多的fd就绪了，就不仅仅 是listenfd就绪了
    {
        for (int i = 0; i < waitnum; i++)
        {
            int tmpfd=_revs[i].data.fd;
            int readyevent = _revs[i].events;
         
            if (tmpfd == _listen_socket->Fd())
            {
                if (readyevent & EPOLLIN)
                {
                    LOG(LogLevel::DEBUG)<<"Accepter";
                    Accepter(); // 连接的获取
                }
            }
            else
            {
                if (readyevent & EPOLLIN)
                {
                    LOG(LogLevel::DEBUG)<<"Recever";
                    Recver(i); // IO的处理
                }
            }
        }
    }
    ~EpollServer()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    int _efd;
    struct epoll_event _revs[revs_num];
};